﻿using QueuesClients;
using QueuesLib;
using System;
using System.Linq;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading.Tasks;

namespace QueuesServer {
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class QueuesService : IInfoPanelService, IKioskService, IWorkStationService {
        private QueuesManager queuesMan;
        private ClientsManager clientsMan;
        private QueuesData.QueuesDataAdapter queuesData;
        private delegate void NoArgsDelegate();

        public QueuesService() {
            queuesMan = new QueuesManager();
            clientsMan = new ClientsManager();
            queuesData = new QueuesData.QueuesDataAdapter();
        }

        #region Service Interfaces implementation

        public string Ping() {
            return "Pong :)";
        }

        public string Register(string clientId) {
            var prop = (RemoteEndpointMessageProperty) OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name];
            var ip = prop.Address;
            //IPv6 addresses require [] around when written directly in URL
            if (ip.Contains(":")) {
                ip = "[" + ip + "]";
            }

            try {
                clientsMan.AddClient(clientId, ip);
            }
            catch (UnauthorizedAccessException e) {
                throw new FaultException<UnauthorizedAccessException>(e, "O id do cliente é desconhecido", new FaultCode("ClientIdError"));
            }
            return "OK";
        }

        public void Unregister(string clientId) {
            clientsMan.RemoveClient(clientId);
            //in case this client isn't a workstation, no problem, EndAttendings has no effect
            queuesMan.EndAttendings(clientId);
        }

        public List<QueueInfo> GetQueuesInfo() {
            return queuesData.QueuesCurrentState.ToList();
        }

        public List<Subject> GetSubjects() {
            return queuesData.Subjects.ToList();
        }

        public List<Counter> GetCounters() {
            return queuesData.Counters.ToList();
        }

        public Ticket GetNewTicket(int subjectId) {
            Ticket ticket;
            try {
                lock (queuesMan) {
                    ticket = queuesMan.NewTicket(subjectId);
                }
            }
            catch (ArgumentException a) {
                throw new FaultException<ArgumentException>(a, "O assunto é desconhecido", new FaultCode("Unknownsubject"));
            }
            Task.Run(() => clientsMan.UpdateAllClientsAsync());
            return ticket;
        }

        public Ticket GetNextTicket(string workStationId, int subjectId) {
            QueueInfo info;
            Ticket ticket;
            try {
                lock (queuesMan) {
                    ticket = queuesMan.CallNext(workStationId, subjectId, out info);
                }
                Task.Run(() => clientsMan.UpdateAllClientsAsync());
                Task.Run(() => clientsMan.NotifyTicketCallAsync(info));
                return ticket;
            }
            catch (ArgumentException a) {
                throw new FaultException<ArgumentException>(a, "O assunto é desconhecido", new FaultCode("UnknownSubject"));
            }
            catch (InvalidOperationException i) {
                throw new FaultException<InvalidOperationException>(i, "A fila está vazia", new FaultCode("EmptyQueue"));
            }
            catch (UnauthorizedAccessException u) {
                throw new FaultException<UnauthorizedAccessException>(u, "Posto de Trabalho desconhecido", new FaultCode("UnknownClient"));
            }

        }

        public Ticket GetNextTicket(string workStationId) {
            return GetNextTicket(workStationId, Subject.ANY_SUBJECT);
        }

        public SubjectInfo GetSubjectInfo(int subjectId) {
            try {
                return queuesMan.GetSubjectInfo(subjectId);
            }
            catch (ArgumentException a) {
                throw new FaultException<ArgumentException>(a, "O assunto é desconhecido", new FaultCode("UnknownSubject"));
            }
        }

        #endregion

    }
}
